/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "imageknife_request_internal.h"
#include "image_data_cache_internal.h"
#include "imageknife_task_internal.h"
#include "imageknife_type.h"
#include "log.h"
#include "task_worker.h"

namespace ImageKnifePro {

bool ImageKnifeRequestInternal::IsMainSrcComplete() const
{
    return status_ == Status::COMPLETE || status_ == Status::ERROR_COMPLETE;
}

void ImageKnifeRequestInternal::MarkStatusComplete()
{
    if (status_ == Status::PROGRESS) {
        status_ = Status::COMPLETE;
    } else if (status_ == Status::ERROR) {
        status_ = Status::ERROR_COMPLETE;
    }
}

void ImageKnifeRequestInternal::Destroy()
{
    if (status_ != Status::COMPLETE) {
        status_ = Status::DESTROY;
        IMAGE_KNIFE_LOG(LOG_DEBUG, "[ID:%{public}s VersionID:%{public}d] Request Destroy",
                        GetNodeId().c_str(), GetVersion());
    }
}

bool ImageKnifeRequestInternal::IsDestroy() const
{
    return status_ == Status::DESTROY;
}

void ImageKnifeRequestInternal::MarkStatusError()
{
    if (status_ == Status::PROGRESS) {
        status_ = Status::ERROR;
    }
}

void ImageKnifeRequestInternal::InsertTask(std::shared_ptr<ImageKnifeTask> task, ImageRequestType type)
{
    taskMap_[type] = task;
}

std::shared_ptr<ImageKnifeTask> ImageKnifeRequestInternal::GetTask(ImageRequestType type)
{
    if (taskMap_.find(type) == taskMap_.end()) {
        return nullptr;
    }
    return taskMap_[type].lock();
}

void ImageKnifeRequestInternal::RemoveTask(ImageRequestType type)
{
    if (taskMap_.find(type) != taskMap_.end()) {
        taskMap_.erase(type);
    }
}

bool ImageKnifeRequestInternal::IsSyncLoad() const
{
    return syncLoad_;
}

void ImageKnifeRequestInternal::SyncLoad(bool syncLoad)
{
    // 限制已执行的请求变更syncLoad状态
    if (executed_) {
        return;
    }
    syncLoad_ = syncLoad;
}

void ImageKnifeRequestInternal::MarkDisplayStartTime(ImageRequestType type, bool fromMemory)
{
    displayType_ = type;
    fromMemory_ = fromMemory;
    displayStart_ = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now());
}

void ImageKnifeRequestInternal::MarkDisplayEndTime()
{
    auto end = std::chrono::high_resolution_clock::now();
    auto totalTime = std::chrono::duration_cast<std::chrono::microseconds>(end - start_);
    auto displayTime = std::chrono::duration_cast<std::chrono::microseconds>(end - displayStart_);

    std::string info = ImageKnifeTaskInternal::GetTaskInfo(displayType_,
        std::static_pointer_cast<ImageKnifeRequestInternal>(shared_from_this()));
    if (fromMemory_) {
        info += " From Memory";
    } else {
        info += " From Loading";
    }

    IMAGE_KNIFE_LOG(LOG_DEBUG, "[%{public}lld] %{public}s Display Finished", displayTime.count(), info.c_str());
    IMAGE_KNIFE_LOG(LOG_DEBUG, "[%{public}lld] %{public}s All Finished (Total Time)", totalTime.count(), info.c_str());
}

std::string ImageKnifeRequestInternal::GetNodeId() const
{
    return componentId_;
}

int ImageKnifeRequestInternal::GetVersion() const
{
    return componentVersion_;
}

ImageKnifeRequest::Status ImageKnifeRequestInternal::GetStatus() const
{
    return status_;
}

std::shared_ptr<ImageKnifeOption> ImageKnifeRequestInternal::GetImageKnifeOption() const
{
    return imageKnifeOption_;
}

bool ImageKnifeRequestInternal::IsPreload() const
{
    return isPreload_;
}

bool ImageKnifeRequestInternal::IsPreloadAnimator() const
{
    return isPreload_ && animator_;
}

void ImageKnifeRequestInternal::SetAnimator(bool animator)
{
    animator_ = animator;
}

void ImageKnifeRequestInternal::SetEnv(napi_env env)
{
    env_ = env;
}

void ImageKnifeRequestInternal::SetJsRequestRef(napi_ref jsRequestRef)
{
    jsRequestRef_ = jsRequestRef;
}

ImageInfo &ImageKnifeRequestInternal::GetImageInfo()
{
    return imageInfo_;
}

void ImageKnifeRequestInternal::ToggleReQueue()
{
    reQueue_ = true;
}

bool ImageKnifeRequestInternal::IsRequeue()
{
    return reQueue_;
}

void ImageKnifeRequestInternal::GetPixelMapInfo(std::shared_ptr<ImageData> imageData)
{
    IMAGE_KNIFE_CHECK(imageData == nullptr);
    OH_PixelmapNative **pixelmapList = imageData->GetPixelmapList();
    IMAGE_KNIFE_CHECK(pixelmapList == nullptr);

    uint32_t width;
    uint32_t height;
    uint32_t frameCount = imageData->GetFrameCount();
    this->imageInfo_.decodeImages.reserve(frameCount);
    if (frameCount > 1) {
        for (int i = 0; i < frameCount; ++i) {
            OH_Pixelmap_ImageInfo *imageInfo = nullptr;
            if (OH_PixelmapImageInfo_Create(&imageInfo) != IMAGE_SUCCESS) {
                continue;
            }

            if (OH_PixelmapNative_GetImageInfo(pixelmapList[i], imageInfo) != IMAGE_SUCCESS) {
                continue;
            }

            OH_PixelmapImageInfo_GetWidth(imageInfo, &width);
            OH_PixelmapImageInfo_GetHeight(imageInfo, &height);
            OH_PixelmapImageInfo_Release(imageInfo);
            this->imageInfo_.decodeImages.push_back({width, height});
        }
    } else {
        OH_Pixelmap_ImageInfo *imageInfo = nullptr;
        if (OH_PixelmapImageInfo_Create(&imageInfo) != IMAGE_SUCCESS) {
            return;
        }

        if (OH_PixelmapNative_GetImageInfo(pixelmapList[0], imageInfo) != IMAGE_SUCCESS) {
            return;
        }

        OH_PixelmapImageInfo_GetWidth(imageInfo, &width);
        OH_PixelmapImageInfo_GetHeight(imageInfo, &height);
        OH_PixelmapImageInfo_Release(imageInfo);
        this->imageInfo_.decodeImages.push_back({width, height});
    }
}

std::string ImageKnifeRequestInternal::GetRequestId() const
{
    return componentId_ + "#" + std::to_string(componentVersion_);
}

bool ImageKnifeRequestInternal::IsMemoryCacheSkiped() const
{
    return cacheType_ != CacheStrategy::MEMORY && cacheType_ != CacheStrategy::DEFAULT;
}

bool ImageKnifeRequestInternal::IsFileCacheSkiped() const
{
    return cacheType_ != CacheStrategy::FILE  && cacheType_ != CacheStrategy::DEFAULT;
}

bool ImageKnifeRequestInternal::IsPreloadCache() const
{
    return promiseFilePath_ != nullptr;
}

void ImageKnifeRequestInternal::ResolvePromiseFilePath(std::string filePath)
{
    if (promiseFilePath_ == nullptr) {
        return;
    }
    promiseFilePath_->Resolve(filePath);
}

void ImageKnifeRequestInternal::ResolvePromiseImageData(std::shared_ptr<ImageData> imageData)
{
    if (promiseImageData_ == nullptr) {
        return;
    }
    promiseImageData_->Resolve(imageData);
}

void ImageKnifeRequestInternal::ResolvePromiseImageData(std::shared_ptr<ImageDataCache> imageDataCache)
{
    if (promiseImageData_ == nullptr) {
        return;
    }
    auto dataCacheInternal = std::dynamic_pointer_cast<ImageDataCacheInternal>(imageDataCache);
    IMAGE_KNIFE_CHECK(dataCacheInternal == nullptr);
    promiseImageData_->Resolve(dataCacheInternal->GetImageData());
}

void ReleaseRef(napi_env env, napi_ref jsRequestRef)
{
    napi_handle_scope scope;
    napi_open_handle_scope(env, &scope);

    napi_value jsRequest = nullptr;
    napi_status status = napi_get_reference_value(env, jsRequestRef, &jsRequest);
    if (status != napi_ok) {
        IMAGE_KNIFE_LOG(LOG_ERROR, "Failed to get jsRequest (Destroy Request)");
        return;
    }

    uint32_t result;
    napi_reference_unref(env, jsRequestRef, &result);
    void *nativeObjectResult = nullptr;
    status = napi_remove_wrap(env, jsRequest, &nativeObjectResult);
    if (status != napi_ok) {
        IMAGE_KNIFE_LOG(LOG_ERROR, "Failed to remove wrap (Destroy Request)");
    }
    napi_close_handle_scope(env, scope);
}

void ImageKnifeRequestInternal::ReleaseRefThreadSafe()
{
    if (TaskWorker::GetInstance()->IsMainThread()) {
        ReleaseRef(env_, jsRequestRef_);
    } else {
        napi_env env = env_;
        napi_ref jsRequestRef = jsRequestRef_;
        auto func = [env, jsRequestRef](void *) {
            ReleaseRef(env, jsRequestRef);
        };
        TaskWorker::GetInstance()->ToMainThread(func, nullptr);
    }
    env_ = nullptr;
    jsRequestRef_ = nullptr;
}

void ImageKnifeRequestInternal::SetComponentSize(Image_Size imageSize)
{
    if (executed_) {
        return;
    }
    requestImageSize_ = imageSize;
}

Image_Size ImageKnifeRequestInternal::GetRequestImageSize()
{
    return requestImageSize_;
}

bool ImageKnifeRequestInternal::MarkExecuted()
{
    if (executed_) {
        return true;
    }
    executed_ = true;
    return false;
}

void ImageKnifeRequestInternal::MarkThumbnailSrcReady()
{
    if (thumbnailStatus_ == ThumbnailStatus::PROGRESS) {
        thumbnailStatus_ = ThumbnailStatus::COMPLETE;
    }
}

void ImageKnifeRequestInternal::MarkThumbnailSrcUnavailable()
{
    if (thumbnailStatus_ == ThumbnailStatus::PROGRESS) {
        thumbnailStatus_ = ThumbnailStatus::UNAVAILABLE;
    }
}

bool ImageKnifeRequestInternal::IsThumbnailSrcReady() const
{
    return thumbnailStatus_ == ThumbnailStatus::COMPLETE;
}

bool ImageKnifeRequestInternal::IsThumbnailSrcUnavailable() const
{
    return thumbnailStatus_ == ThumbnailStatus::UNAVAILABLE;
}

} // end of namespace
